﻿namespace NTLib.Core.UTest.Reflection
{
    using Core.Reflection;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    [TestClass]
    public class ObjectDescriptorUTest
    {
        #region Nested

        public class TestObject
        {
            public string NameString { get; set; }

            public Guid id { get; set; }

            public DateTime Date { get; set; }

            public TimeSpan Time { get; set; }

            public int Toto { get; set; }

            public NTestStruct Struct { get; set; }

            public NTestObject obj { get; set; }

            public NTestStruct[] Structs { get; set; }

            public List<NTestObject> Objects { get; set; }

            public List<string> Strings { get; set; }

            public NTestObjectDescriptor Auto { get; set; }
            public List<NTestObjectDescriptor> AutoCollection { get; set; }

            public TestObject Recursif { get; set; }
        }

        public struct NTestStruct
        {
            public string Test { get; set; }
        }

        public class NTestObject
        {
            public string Test { get; set; }
        }

        public class NTestObjectDescriptor : IObjectDescriptor
        {
            public string Test { get; set; }

            public ObjectDescriptor Dump(string name = null)
            {
                var obj = ObjectDescriptor.Pool.Get<ObjectDescriptor>();
                var value = ValueDescriptor.Pool.Get<ValueDescriptor>();
                value.Initialize(typeof(int), "DumpOverride");
                value.Value = 42.ToString();
                obj.Initialize(typeof(NTestObjectDescriptor), name);
                obj.Properties = new List<IElementDescriptor> { value };
                return obj;
            }
        }

        #endregion

        #region Methods

        [TestCategory(ReflectionUTest.CATEGORY)]
        [TestMethod]
        public void ObjectDescription_AutomaticCreation_Default()
        {
            const int intValue = 42;

            TimeSpan time = TimeSpan.FromSeconds(42);
            TestObject obj = new TestObject();
            var date = DateTime.Now;
            obj.Time = time;
            obj.Toto = intValue;
            obj.Date = date;

            var desc = ObjectDescriptor.Create(obj) as ObjectDescriptor;
            Assert.IsNotNull(desc);
            Assert.AreEqual(null, desc.Name);
            Assert.AreEqual(typeof(TestObject).Name, desc.TypeName);
            Assert.AreEqual(typeof(TestObject).AssemblyQualifiedName, desc.AssemblyQualifiedName);

            Assert.IsNotNull(desc.Properties);
            Assert.AreEqual(13, desc.Properties.Count);

            var timeProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Time)) as ValueDescriptor;
            var totoProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Toto)) as ValueDescriptor;
            var dateProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Date)) as ValueDescriptor;
            var structsProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Structs)) as ValueDescriptor;

            var structProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Struct)) as ObjectDescriptor;
            var objProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.obj)) as ValueDescriptor;

            Assert.IsNotNull((object)timeProp);
            Assert.IsNotNull((object)totoProp);
            Assert.IsNotNull((object)dateProp);
            Assert.IsNotNull((object)structsProp);

            Assert.IsNotNull((object)structProp);
            Assert.IsNotNull((object)objProp);

            Assert.IsTrue(!string.IsNullOrEmpty(timeProp.Value));
            Assert.IsTrue(!string.IsNullOrEmpty(totoProp.Value));
            Assert.IsTrue(!string.IsNullOrEmpty(dateProp.Value));
            Assert.AreEqual("(null)", structsProp.Value);

            Assert.AreEqual(timeProp.Value, time.ToString());
            Assert.AreEqual(totoProp.Value, intValue.ToString());
            Assert.AreEqual(dateProp.Value, date.ToString());
        }


        /// <summary>
        /// Objects the description i object descriptor.
        /// </summary>
        [TestCategory(ReflectionUTest.CATEGORY)]
        [TestMethod]
        public void ObjectDescription_IObjectDescriptor()
        {
            TestObject obj = new TestObject();
            obj.Auto = new NTestObjectDescriptor();

            var desc = ObjectDescriptor.Create(obj) as ObjectDescriptor;

            Assert.IsNotNull(desc.Properties);
            Assert.AreEqual(13, desc.Properties.Count);

            var autoProp = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Auto)) as ObjectDescriptor;

            Assert.IsNotNull(autoProp);
            Assert.IsNotNull(autoProp.Properties);
            Assert.AreEqual(1, autoProp.Properties.Count);

            var value = autoProp.Properties.FirstOrDefault(p => p.Name == "DumpOverride") as ValueDescriptor;

            Assert.IsNotNull(value);
            Assert.AreEqual(value.Value, 42.ToString());
        }

        /// <summary>
        /// Objects the description collection.
        /// </summary>
        [TestCategory(ReflectionUTest.CATEGORY)]
        [TestMethod]
        public void ObjectDescription_Collection()
        {
            TestObject obj = new TestObject();
            obj.Strings = new List<string>() { "test", "test2" };
            obj.AutoCollection = new List<NTestObjectDescriptor>() { new NTestObjectDescriptor() };
            obj.Objects = new List<NTestObject>() { new NTestObject() };
            obj.Structs = new NTestStruct[] { new NTestStruct() };

            var desc = ObjectDescriptor.Create(obj) as ObjectDescriptor;

            var strings = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Strings)) as CollectionDescriptor;
            var autoCollection = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.AutoCollection)) as CollectionDescriptor;
            var objects = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Objects)) as CollectionDescriptor;
            var structs = desc.Properties.FirstOrDefault(p => p.Name == nameof(TestObject.Structs)) as CollectionDescriptor;

            Assert.IsNotNull(strings);
            Assert.IsNotNull(autoCollection);
            Assert.IsNotNull(objects);
            Assert.IsNotNull(structs);

            Assert.IsNotNull(strings.Elements);
            Assert.IsNotNull(autoCollection.Elements);
            Assert.IsNotNull(objects.Elements);
            Assert.IsNotNull(structs.Elements);

            Assert.AreEqual(2, strings.Elements.Count);
            Assert.AreEqual(1, autoCollection.Elements.Count);
            Assert.AreEqual(1, objects.Elements.Count);
            Assert.AreEqual(1, structs.Elements.Count);
        }

        /// <summary>
        /// Objects the description recursif.
        /// </summary>
        [TestCategory(ReflectionUTest.CATEGORY)]
        [TestMethod]
        public void ObjectDescription_Recursif()
        {
            TestObject obj = new TestObject();
            TestObject obj2 = new TestObject();

            List<TestObject> objs = new List<TestObject>();

            obj2.Recursif = obj;
            obj.Recursif = obj2;

            objs.Add(obj);
            objs.Add(obj2);

            var haveFun = ObjectDescriptor.ReflectionCreate(objs);
        }

        #endregion
    }
}
